package com.fastjrun.codeg.generator.method;

import java.util.ArrayList;
import java.util.List;

import com.fastjrun.codeg.common.CommonController;
import com.fastjrun.codeg.common.PacketField;
import com.fastjrun.codeg.generator.BaseRPCGenerator;
import com.fastjrun.codeg.helper.StringHelper;
import com.sun.codemodel.JBlock;
import com.sun.codemodel.JClass;
import com.sun.codemodel.JDefinedClass;
import com.sun.codemodel.JExpr;
import com.sun.codemodel.JExpression;
import com.sun.codemodel.JInvocation;
import com.sun.codemodel.JMethod;
import com.sun.codemodel.JMod;
import com.sun.codemodel.JType;
import com.sun.codemodel.JVar;

public abstract class BaseRPCMethodGenerator extends BaseControllerMethodGenerator {

    protected JMethod japiMethod;

    protected JMethod japiManagerMethod;

    public void processApiMethod(JDefinedClass apiClass) {
        this.japiMethod = apiClass.method(JMod.NONE, this.exchangeProcessor.getResponseClass(),
                this.serviceMethodGenerator.getMethodName());
        String methodRemark = this.serviceMethodGenerator.getCommonMethod().getRemark();
        this.japiMethod.javadoc().append(methodRemark);

        MethodGeneratorHelper
                .processServiceMethodVariables(this.japiMethod, this.serviceMethodGenerator.getCommonMethod()
                        .getHeadVariables());
        MethodGeneratorHelper.processServiceMethodVariables(this.japiMethod,
                this.serviceMethodGenerator.getCommonMethod().getPathVariables());
        MethodGeneratorHelper.processServiceMethodVariables(this.japiMethod,
                this.serviceMethodGenerator.getCommonMethod().getParameters());
        MethodGeneratorHelper.processServiceMethodVariables(this.japiMethod,
                this.serviceMethodGenerator.getCommonMethod().getCookieVariables());

        if (this.exchangeProcessor.getRequestClass() != null) {
            this.japiMethod.param(this.exchangeProcessor.getRequestClass(), "request");
        }
    }

    public void processApiManagerMethod(CommonController commonController, JDefinedClass apiManagerClass) {
        this.japiManagerMethod = apiManagerClass.method(JMod.PUBLIC, this.exchangeProcessor.getResponseClass(),
                this.serviceMethodGenerator.getMethodName());
        String methodRemark = this.serviceMethodGenerator.getCommonMethod().getRemark();
        this.japiManagerMethod.javadoc().append(methodRemark);
        String serviceName = commonController.getServiceName();
        JInvocation jInvocation = JExpr.invoke(JExpr.refthis(serviceName), this.serviceMethodGenerator.getMethodName());

        List<PacketField> headVariables = this.serviceMethodGenerator.getCommonMethod().getHeadVariables();
        if (headVariables != null && headVariables.size() > 0) {
            for (int index = 0; index < headVariables.size(); index++) {
                PacketField headVariable = headVariables.get(index);
                JType jType = cm.ref(headVariable.getDatatype());
                JVar headVariableJVar = this.japiManagerMethod.param(jType, headVariable.getName());
                jInvocation.arg(headVariableJVar);
            }
        }

        List<PacketField> pathVariables = this.serviceMethodGenerator.getCommonMethod().getPathVariables();
        if (pathVariables != null && pathVariables.size() > 0) {
            for (int index = 0; index < pathVariables.size(); index++) {
                PacketField pathVariable = pathVariables.get(index);
                JType jType = cm.ref(pathVariable.getDatatype());
                JVar pathVariableJVar = this.japiManagerMethod.param(jType, pathVariable.getName());
                jInvocation.arg(pathVariableJVar);
            }
        }

        List<PacketField> parameters = this.serviceMethodGenerator.getCommonMethod().getParameters();
        if (parameters != null && parameters.size() > 0) {
            for (int index = 0; index < parameters.size(); index++) {
                PacketField parameter = parameters.get(index);
                JClass jClass = cm.ref(parameter.getDatatype());
                JVar parameterJVar = this.japiManagerMethod.param(jClass, parameter.getName());
                jInvocation.arg(parameterJVar);
            }
        }

        List<PacketField> cookieVariables = this.serviceMethodGenerator.getCommonMethod().getCookieVariables();
        if (cookieVariables != null && cookieVariables.size() > 0) {
            for (int index = 0; index < cookieVariables.size(); index++) {
                PacketField cookieVariable = cookieVariables.get(index);
                JType jType = cm.ref(cookieVariable.getDatatype());
                JVar cookieJVar = this.japiManagerMethod.param(jType, cookieVariable.getName());
                jInvocation.arg(cookieJVar);
            }
        }

        this.exchangeProcessor.processRPCRequest(this.japiManagerMethod, jInvocation);
        this.exchangeProcessor.processResponse(this.japiManagerMethod.body(), jInvocation);
    }

    public void processClientMethod(JClass apiClass, JDefinedClass clientClass) {
        this.jClientMethod = clientClass.method(JMod.PUBLIC, this.serviceMethodGenerator.getResponseBodyClass(),
                this.serviceMethodGenerator.getMethodName());
        String methodRemark = this.serviceMethodGenerator.getCommonMethod().getRemark();
        this.jClientMethod.javadoc().append(methodRemark);
        JBlock methodBlk = this.jClientMethod.body();
        String invokeMethodName;
        if (this.serviceMethodGenerator.getCommonMethod().isResponseIsArray()) {
            invokeMethodName = "processList";
        } else {
            invokeMethodName = "process";
        }
        JInvocation jInvocation = JExpr.invoke(JExpr.ref("baseClient"), invokeMethodName);
        jInvocation.arg(JExpr.dotclass(apiClass));
        jInvocation.arg(JExpr.lit(this.serviceMethodGenerator.getMethodName()));
        List<JType> paramterTypes = new ArrayList<>();

        List<JVar> paramterJVars = new ArrayList<>();

        // headParams
        List<PacketField> headVariables = this.serviceMethodGenerator.getCommonMethod().getHeadVariables();
        if (headVariables != null && headVariables.size() > 0) {
            for (int index = 0; index < headVariables.size(); index++) {
                PacketField headVariable = headVariables.get(index);
                JClass jClass = cm.ref(headVariable.getDatatype());
                JVar headJVar = this.jClientMethod.param(jClass, headVariable.getNameAlias());
                paramterTypes.add(jClass);
                paramterJVars.add(headJVar);

                methodBlk.invoke(JExpr.ref("log"), "debug").arg(JExpr.lit("header[{}] = {}"))
                        .arg(JExpr.lit(headVariable.getNameAlias()))
                        .arg(JExpr.ref(headVariable.getNameAlias()));

            }

        }

        List<PacketField> pathVariables = this.serviceMethodGenerator.getCommonMethod().getPathVariables();
        if (pathVariables != null && pathVariables.size() > 0) {
            for (int index = 0; index < pathVariables.size(); index++) {
                PacketField pathVariable = pathVariables.get(index);
                JClass jClass = cm.ref(pathVariable.getDatatype());
                JVar pathVariableVar = this.jClientMethod.param(jClass, pathVariable.getName());
                paramterTypes.add(jClass);
                paramterJVars.add(pathVariableVar);

                methodBlk.invoke(JExpr.ref("log"), "debug").arg(JExpr.lit("pathVariable[{}] = {}"))
                        .arg(JExpr.lit(pathVariable.getName())).arg(JExpr.ref(pathVariable.getName()));

            }
        }

        List<PacketField> parameters = this.serviceMethodGenerator.getCommonMethod().getParameters();
        if (parameters != null && parameters.size() > 0) {
            for (int index = 0; index < parameters.size(); index++) {
                PacketField parameter = parameters.get(index);
                JClass jClass = cm.ref(parameter.getDatatype());
                JVar parameterVar = this.jClientMethod.param(jClass, parameter.getName());
                paramterTypes.add(jClass);
                paramterJVars.add(parameterVar);

                methodBlk.invoke(JExpr.ref("log"), "debug").arg(JExpr.lit("paramter[{}] = {}"))
                        .arg(JExpr.lit(parameter.getName())).arg(JExpr.ref(parameter.getName()));

            }
        }

        List<PacketField> cookies = this.serviceMethodGenerator.getCommonMethod().getCookieVariables();
        if (cookies != null && cookies.size() > 0) {
            for (int index = 0; index < cookies.size(); index++) {
                PacketField cookie = cookies.get(index);
                JClass jClass = cm.ref(cookie.getDatatype());
                JVar cookieVar = this.jClientMethod.param(jClass, cookie.getName());
                paramterTypes.add(jClass);
                paramterJVars.add(cookieVar);

                JExpression jInvocationCookie = JExpr.ref(cookie.getName());
                methodBlk.invoke(JExpr.ref("log"), "debug").arg(JExpr.lit("paramter[{}] = {}"))
                        .arg(JExpr.lit(cookie.getName())).arg(JExpr.ref(cookie.getName()));

            }
        }

        if (this.serviceMethodGenerator.getRequestBodyClass() != null) {
            JVar jRequestBodyVar = this.jClientMethod.param((this.serviceMethodGenerator.getRequestBodyClass()),
                    "requestBody");
            paramterTypes.add(this.exchangeProcessor.getRequestClass());
            paramterJVars.add(jRequestBodyVar);
        }

        if (paramterTypes.size() > 0) {
            JVar paramterTypesJVar = methodBlk.decl(cm.ref("Class").array(), "paramterTypes", JExpr.newArray(cm.ref
                    ("Class"), paramterTypes.size()));
            JVar paramterValuesJVar = methodBlk.decl(cm.ref("Object").array(), "paramterValues", JExpr.newArray(cm
                    .ref
                            ("Object"), paramterJVars.size()));
            for (int i = 0; i < paramterJVars.size(); i++) {
                methodBlk.assign(paramterTypesJVar.component(JExpr.lit(i)), paramterJVars.get(i).invoke
                        ("getClass"));
                methodBlk.assign(paramterValuesJVar.component(JExpr.lit(i)), paramterJVars.get(i));
            }
            jInvocation.arg(paramterTypesJVar);
            jInvocation.arg(paramterValuesJVar);
        }

        if (this.serviceMethodGenerator.getResponseBodyClass() != null && this.serviceMethodGenerator
                .getResponseBodyClass() != cm.VOID) {
            methodBlk._return(jInvocation);
        } else {
            methodBlk.add(jInvocation);
        }
    }

    @Override
    public void generate() {
        if (this.baseControllerGenerator.getCommonController().is_new()) {
            this.processApiMethod((JDefinedClass) ((BaseRPCGenerator) this.baseControllerGenerator).getApiClass());
        }
        if (!this.isApi()) {
            if (this.isClient()) {
                this.processClientMethod(((BaseRPCGenerator) this.baseControllerGenerator).getApiClass(), this
                        .baseControllerGenerator.getClientClass());
                this.processClientTestMethod(this
                        .baseControllerGenerator.getClientTestClass());
                StringBuilder sb = new StringBuilder(this.baseControllerGenerator.getClientName()).append("Test.test");
                sb.append(StringHelper.toUpperCaseFirstOne(this.serviceMethodGenerator.getMethodName())).append(".n");
                this.processClientTestPraram();
                this.baseControllerGenerator.getClientTestParam().put(sb.toString(), this.methodParamInJsonObject
                        .toString().replaceAll("\n", "").replaceAll("\r", "")
                        .trim());
            } else {
                if (this.getMockModel() != MockModel.MockModel_Common) {
                    this.processControllerMethod(this.baseControllerGenerator.getCommonController(), this
                            .baseControllerGenerator.getControlllerClass());
                }

                this.processApiManagerMethod(this.baseControllerGenerator.getCommonController(),
                        ((BaseRPCGenerator) this
                                .baseControllerGenerator).getApiManagerClass());
            }
        }

    }
}